/********************************************************************************
 * Copyright (c) 2020 [Open Lowcode SAS](https://openlowcode.com/)
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0 .
 *
 * SPDX-License-Identifier: EPL-2.0
 ********************************************************************************/

package org.openlowcode.tools.stats;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

import org.openlowcode.OLcVersionGenerator;


/**
 * An utility to count lines of codes and store it in a stats file
 * 
 * @author <a href="https://openlowcode.com/" rel="nofollow">Open Lowcode
 *         SAS</a>
 *
 */
public class CountLinesOfCode {
	
	private static long[] linesofcode;
	private static long linesofcomment= 0;
	private static long generatedlinesofcode = 0;
	private static long generatedlinesofcomment = 0;
	private static long codegeneration=0;
	private static long absolutetotallines=0;
	private static long totalnonblanklines=0;
	
	private static void addlineofcode(boolean autogenerated,int index) {
		if (autogenerated) {
			generatedlinesofcode++;
		} else {
			linesofcode[index]++;
		}
	}
	private static void addlineofcomment(boolean autogenerated) {
		if (autogenerated) {
			generatedlinesofcomment++;
		} else {
			linesofcomment++;
		}
	}
	
	private static void processsourcefile(File thisfile,int index) throws Exception {
		BufferedReader filereader = new BufferedReader(new FileReader(thisfile));
		String nextline = filereader.readLine();
		boolean autogenerated=false;
		boolean insidemultilinecomment = false;
		
		if (nextline!=null) {
			nextline = nextline.trim();
			if (nextline.startsWith("/*-OLc-OLc-OLc")) autogenerated=true;
			if (nextline.startsWith("/*")) if (nextline.indexOf("*/")==-1) insidemultilinecomment=true;
				
		}
		
		while (nextline!=null) {
			absolutetotallines++;
			// only process lines with content;
			if (nextline.length()>0) {
				totalnonblanklines++;
				// inside comment
				if (insidemultilinecomment) {
					// inside multiline comment with no closing
					if (nextline.indexOf("*/")==-1) {
						addlineofcomment(autogenerated);
					} else {
						addlineofcomment(autogenerated);
						insidemultilinecomment=false;
					
					}
					
				} else {
					if ((nextline.indexOf("/*")==-1)) {
						if (nextline.startsWith("//")) {
							addlineofcomment(autogenerated);
						} else {
							if ((nextline.indexOf("sg.w(")!=-1) || (nextline.indexOf("sg.wl(")!=-1)) codegeneration++;
							addlineofcode(autogenerated,index);
							if (nextline.indexOf("//")!=- 	1) addlineofcomment(autogenerated);
						}
					} else {
						 if (!nextline.startsWith("/*")) addlineofcode(autogenerated,index);
						addlineofcomment(autogenerated);
						if (nextline.lastIndexOf("*/")<nextline.lastIndexOf("/*")) insidemultilinecomment=true;
					}
			}
			
			
		}
			nextline = filereader.readLine();
			if (nextline!=null) nextline = nextline.trim();
		}
		filereader.close();
	}
	private static boolean isValidFileType(File thisfile) throws Exception {
		String filename = thisfile.getName();
		if (filename.toLowerCase().endsWith(".java")) return true;
		return false;
	}
	private static void processfolder(File currentfolder,int recursivebreaker,int index) throws Exception {
		if (recursivebreaker>1024) throw new RuntimeException("Recursive breaker for path "+currentfolder.getPath());
		System.err.print("Treating folder "+currentfolder.getAbsolutePath()+" ");
		File[] children = currentfolder.listFiles();
		int nbfile=0;
		for (int i=0;i<children.length;i++) {
			File thischild = children[i];
			if (!thischild.isDirectory()) if (isValidFileType(thischild)) {
				System.err.print(".");
				nbfile++;
				 processsourcefile(thischild,index);
			}
		}
		System.err.println(" "+nbfile+" file(s)");
		for (int i=0;i<children.length;i++) {
			File thischild = children[i];
			if (thischild.isDirectory()) {
				processfolder(thischild,recursivebreaker+1,index);
			} 
		}
	}
	
	public static void main(String args[]) {
		if (args.length!=2) {
			System.err.println("Error: Syntax java CountLinesOfCode sourcepath file");
			System.err.println("	Where");
			System.err.println("	sourcepath is absolute or relative path to start counting lines of code : typically ./src/. For several pathes, separate them by semi-column ';'");
			System.err.println("	file is the file to log the results (date, Open Lowcode version, number of lines, number of comments, number of lines autogenerated, number of comments autogenerated");
			System.exit(1);
		}
		String sourcepathregrouped = args[0];
		String[] sourcepathes = sourcepathregrouped.split(";");
		linesofcode = new long[sourcepathes.length];
		for (int i=0;i<sourcepathes.length;i++) linesofcode[i]=0;
		String resultfiletext = args[1];
		try {
			for (int i=0;i<sourcepathes.length;i++) {
			File sourcepathfile = new File(sourcepathes[i]);
			if (!sourcepathfile.exists()) throw new RuntimeException("Source Path "+sourcepathes[i]+" does not exits");
			if (!sourcepathfile.isDirectory()) throw new RuntimeException("Source Path "+sourcepathes[i]+" is not a directory");
			File currentfolder = sourcepathfile;
			processfolder(currentfolder,0,i);
			}
			ArrayList<String> linestokeep = new ArrayList<String>();
			
			File resultfile = new File(resultfiletext);
			if (resultfile.exists()) {
			BufferedReader resultfilereader = new BufferedReader(new FileReader(resultfile));
			
			Iterator<String> filelines = resultfilereader.lines().iterator();
			filelines.next(); // discard header
			String lastdate = "nodate";
			while (filelines.hasNext()) {
				String thisline = filelines.next();
				if (thisline.length()>19) { // is at least long enough to have date
					String date = thisline.substring(0,11);
					if (!date.equals(lastdate)) {
						lastdate=date;
						linestokeep.add(thisline);
					}
				}
			}
			resultfilereader.close();
			}
			if (resultfile.exists()) {
				if (resultfile.isDirectory()) throw new RuntimeException("Result File "+resultfiletext+" is actually a directory");
			}
			BufferedWriter writer = new BufferedWriter(new FileWriter(resultfile));
			String lines = "";
			for (int i=0;i<sourcepathes.length;i++) lines+="Lines "+i+",";
			writer.write("Date,Open Lowcode Version,"+lines+"Comments,Autogenerated,Comments AutoGenerated,Code Generation,All lines,All non blank lines\n");
			for (int i=0;i<linestokeep.size();i++) {
				writer.write(linestokeep.get(i)+"\n");
				
			}
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
			StringBuffer linetowrite = new StringBuffer();
			linetowrite.append(sdf.format(new Date()));
			linetowrite.append(",");
			linetowrite.append(OLcVersionGenerator.version);
			linetowrite.append(",");
			for (int i=0;i<linesofcode.length;i++) {
				linetowrite.append(linesofcode[i]);
				linetowrite.append(",");
			}
			linetowrite.append(linesofcomment);
			linetowrite.append(",");
			linetowrite.append(generatedlinesofcode);
			linetowrite.append(",");
			linetowrite.append(generatedlinesofcomment);
			linetowrite.append(",");
			linetowrite.append(codegeneration);
			linetowrite.append(",");
			linetowrite.append(absolutetotallines);
			linetowrite.append(",");
			linetowrite.append(totalnonblanklines);
			linetowrite.append("\n");
			writer.write(linetowrite.toString());
			writer.close();
		} catch (Exception e) {
			System.err.println("Error during execution "+e.getMessage());
			e.printStackTrace(System.err);
			System.exit(1);
		}
	}
}
